local ffi = require 'ffi'
local math_ext = require 'math_extended'
local vec2 = require 'vec2'

ffi.cdef [[
    typedef struct { vec2_t center; vec2_t extents; } rect_t;
]]

local rect

---@class rect_mt
local mt = {
    ---@param center vec2
    ---@param size vec2
    ---@return rect
    from_center = function(center, size)
        return rect(center, size * 0.5)
    end,

    ---@param bottom_left vec2
    ---@param size vec2
    ---@return rect
    from_bottom_left = function(bottom_left, size)
        local extents = size * 0.5
        return rect(bottom_left + extents, extents)
    end,

    ---@param top_left vec2
    ---@param size vec2
    ---@return rect
    from_top_left = function(top_left, size)
        local extents = size * 0.5
        return rect(vec2(top_left.x + extents.x, top_left.y - extents.y), extents)
    end,

    ---@param top_right vec2
    ---@param size vec2
    ---@return rect
    from_top_right = function(top_right, size)
        local extents = size * 0.5
        return rect(top_right - extents, extents)
    end,

    ---@param bottom_right vec2
    ---@param size vec2
    ---@return rect
    from_bottom_right = function(bottom_right, size)
        local extents = size * 0.5
        return rect(vec2(bottom_right.x - extents.x, bottom_right.y + extents.y), extents)
    end,

    ---@param self rect
    ---@return vec2
    size = function(self) return self.extents * 2 end,

    ---@param self rect
    ---@return vec2
    bottom_left = function(self)
        return self.center - self.extents
    end,

    ---@param self rect
    ---@return vec2
    top_left = function(self)
        return vec2(self.center.x - self.extents.x, self.center.y + self.extents.y)
    end,

    ---@param self rect
    ---@return vec2
    top_right = function(self)
        return self.center + self.extents
    end,

    ---@param self rect
    left = function(self)
        return self.center.x - self.extents.x
    end,

    ---@param self rect
    right = function(self)
        return self.center.x + self.extents.x
    end,

    ---@param self rect
    top = function(self)
        return self.center.y + self.extents.y
    end,

    ---@param self rect
    bottom = function(self)
        return self.center.y - self.extents.y
    end,

    ---@param self rect
    ---@return vec2
    bottom_right = function(self)
        return vec2(self.center.x + self.extents.x, self.center.y - self.extents.y)
    end,

    ---@param self rect
    ---@param parent_rect rect
    ---@param alignment vec2
    ---@return rect
    align_in = function(self, parent_rect, alignment)
        local parent_lerp = math_ext.lerp(
            parent_rect:bottom_left(), parent_rect:top_right(), alignment
        )

        local rect_offset = self.center - alignment * self:size()

        return rect(parent_lerp + rect_offset, self.extents)
    end,

    ---@param self rect
    ---@return rect
    copy = function(self) return rect(self.center, self.extents) end,
}
mt.__index = mt

---@class rect : rect_mt
---@field center vec2
---@field extents vec2
rect = ffi.metatype("rect_t", mt)

return rect
